Skip to content

feat: add viz poc 1#963

Open
nicolaskempf57 wants to merge 122 commits intomainfrom
feat_add_viz_poc_1
Open

feat: add viz poc 1#963
nicolaskempf57 wants to merge 122 commits intomainfrom
feat_add_viz_poc_1

Conversation

@nicolaskempf57
Copy link
Copy Markdown
Contributor

@nicolaskempf57 nicolaskempf57 commented Feb 26, 2026

- Convert resources to computed from savedResources
- Add loadChart() to fetch chart and extract resource IDs from series
- Add loadMissingResourcesForChart() to fetch individual resources
- Add loadSelectedChart() triggered by Charger button
- Switch form from reactive to ref
- Move dataset initialization to top level
- Fetch charts list at top level instead of onMounted
Comment thread datagouv-components/src/components/Chart/ChartViewerWrapper.vue Outdated
Comment thread datagouv-components/src/components/Chart/ChartViewerWrapper.vue Outdated
Comment thread datagouv-components/src/components/Chart/ChartViewer.vue Outdated
Comment thread datagouv-components/src/components/Chart/ChartViewer.vue Outdated
Comment on lines +212 to +219
watch(() => props.chart.series, async () => {
await fetchSeriesProfile()
}, { immediate: true, deep: true })

// Watch for changes in the chart or its series
watch([() => props.chart.series, () => props.chart.x_axis.column_x], async () => {
await fetchSeriesData()
}, { immediate: true, deep: true })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchSeriesProfile and fetchSeriesData run in parallel I think and they both reset and write in status.value and error.value, one error followed by one success will override the status?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a simple count to have a success only when all succeed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it solves all the problems with this design. Setting status = 'success' after fetching the profile will show a flash of the chart without dataset then fetchSeriesData resolve and the chart is populated? Maybe remove status = 'success' and let the watch(pendingOperation) set it to be sure the two fetches are done before setting the status or maybe refactor this logic to be simpler? fetchSeriesProfile and fetchSeriesData are only called in these watchers? You may merge them together and call with a Promise.all(). If you don't want to get the profile on props.chart.x_axis.column_x you may just have one function with an argument dataOnly=true, it will solves all these problems, no?

Comment thread components/Charts/ChartConfigurator.vue Outdated
Comment thread components/Charts/ChartConfigurator.vue Outdated
Comment thread components/Charts/ChartConfigurator.vue Outdated
Comment thread datagouv-components/src/components/Chart/ChartViewer.vue Outdated
Comment thread datagouv-components/src/components/Chart/ChartViewer.vue Outdated
Comment on lines +75 to +81
<button
class="fr-btn"
:disabled="!selectedChartId"
@click="loadSelectedChart"
>
{{ $t('Charger') }}
</button>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing type="button", no?

const formatter = new Intl.NumberFormat('fr-FR')
for (const param of params) {
const seriesName = param.seriesName
const seriesConfig = props.chart.series.find(s => s.column_y === seriesName)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is two series with the same column name (two different aggregate sum/mean for exemple), you'll always get the first one and then ${seriesName}__${seriesConfig.aggregate_y} could match nothing? (the seriesName from the one you hover but the seriesConfig.aggregate_y from the first serie?

Comment on lines +212 to +219
watch(() => props.chart.series, async () => {
await fetchSeriesProfile()
}, { immediate: true, deep: true })

// Watch for changes in the chart or its series
watch([() => props.chart.series, () => props.chart.x_axis.column_x], async () => {
await fetchSeriesData()
}, { immediate: true, deep: true })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it solves all the problems with this design. Setting status = 'success' after fetching the profile will show a flash of the chart without dataset then fetchSeriesData resolve and the chart is populated? Maybe remove status = 'success' and let the watch(pendingOperation) set it to be sure the two fetches are done before setting the status or maybe refactor this logic to be simpler? fetchSeriesProfile and fetchSeriesData are only called in these watchers? You may merge them together and call with a Promise.all(). If you don't want to get the profile on props.chart.x_axis.column_x you may just have one function with an argument dataOnly=true, it will solves all these problems, no?

}

function removeFilter(index: number) {
if (!form.value.filter || !('filters' in form.value.filter)) return
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removeFilter returns early when form.value.filter is a bare Filter
(not an AndFilters), but filterList (line 597) still renders a row
with its delete button in that case — clicking it is a silent no-op.

The form lands in this state when loading an existing chart whose
series[0].filters is a bare Filter: toChartForm (charts.ts:4)
casts it as Filter | null and assigns it directly to
form.value.filter, even though DataSeries.filters is typed as
GenericFilter and can be either shape at runtime.

Two options:

  • normalize in toChartForm so form.value.filter is always either
    null or an AndFilters (then filterList and removeFilter lose
    the dual-shape branching),

  • or handle the bare-Filter case here:

    if (!form.value.filter) return
    if (!('filters' in form.value.filter)) {
    if (index === 0) form.value.filter = null
    return
    }
    form.value.filter.filters.splice(index, 1)
    ...

The normalization in toChartForm is cleaner.

Comment on lines +72 to +73
if (valA === null && valB === null) return 0
if (valA === undefined && valB === undefined) return 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "both null" / "both undefined" checks on lines 72-73 are unreachable:
lines 70-71 already return as soon as either side is null/undefined, so
when both values are null the comparator returns -1 (asc) instead of 0 —
a violation of comparator equality. Modern engines use a stable sort so
the practical impact is limited, but the dead checks reveal the intent
doesn't match the behavior.

Either drop lines 72-73, or (cleaner) handle equality first:

const aNullish = valA === null || valA === undefined
const bNullish = valB === null || valB === undefined
if (aNullish && bNullish) return 0
if (aNullish) return sortDirection === 'asc' ? -1 : 1
if (bNullish) return sortDirection === 'asc' ? 1 : -1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

📊 Datastudio sur data.gouv.fr

3 participants